#include "TcpConn.h"
#include "MessageBuffer.h"
#include "IPoller.h"

TcpConn::TcpConn(int fd, IPoller* poller) : fd_(fd), poller_(poller), closed_(false) {
    SetNonBlocking(fd_);
    io_handle_ = [this](int events) {
        HandleIO(events);
    };
    poller_->AddEvent(fd_, EPOLLIN | EPOLLRDHUP, &io_handle_);
}

TcpConn::~TcpConn() {
    Close();
}

int TcpConn::Send(const char* data, size_t size) {
    if(closed_ || data == nullptr || size == 0) {
        return -1;
    }

    if(!output_buffer.empty()) {
        output_buffer.append(data, size);
        EnableWrite();
        return size;
    }

    int n = ::send(fd_, data, size, MSG_NOSIGNAL);
    if(n < 0) {
        if(errno == EAGAIN || errno == EWOULDBLOCK) {
            output_buffer.append(data, size);
            EnableWrite();
        }
        else
        {
            Close();
        }
    }
    else
    {
        if(n < size)
        {
            output_buffer.append(data + n, size - n);
            EnableWrite();
        }
    }
    return n;
}

void TcpConn::HandleIO(int events) {
    if(closed_) {
        return;
    }

    if(events & EPOLLIN) {
        HandleRead();
    }
    if(events & EPOLLOUT) {
        HandleWrite();
    }
    if(events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) {
        Close();
    }
}

void TcpConn::HandleRead()
{
    int err = 0;
    int n = input_buffer_.Recv(fd, &err);
    if(n > 0)
    {
        if(read_cb_)
        {
            read_cb_();
        }
    }
    else if(n == 0 (n < 0 && (err != EAGAIN && err != EWOULDBLOCK)))
    {
        Close();
    }
} 

void TcpConn::HandleWrite()
{
    int n = ::send(fd_, output_buffer.data(), output_buffer.size(), MSG_NOSIGNAL);
    if(n > 0) {
        output_buffer.erase(0, n);
        if(output_buffer.empty()) {
            DisableWrite();
        }
    }
    else if(n < 0 && (errno != EAGAIN && errno != EWOULDBLOCK)) {
        Close();
    }
}

void TcpConn::Close() {
    if(closed_) {
        return;
    }
    closed_ = true;
    poller_->DelEvent(fd_);
    ::close(fd_);
}

void TcpConn::EnableWrite() {
    poller_->ModEvent(fd_, EPOLLIN | EPOLLOUT | EPOLLRDHUP, this);
}

void TcpConn::DisableWrite() {
    poller_->ModEvent(fd_, EPOLLIN | EPOLLRDHUP, this);
}

void TcpConn::SetNonBlocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    if(flags < 0) {
        std::cerr << "fcntl F_GETFL failed" << std::endl;
        return;
    }
    if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
        std::cerr << "fcntl F_SETFL failed" << std::endl;
    }
}

std::string TcpConn::GetAllData() {
    return input_buffer_.GetAllData();
}

std::string TcpConn::GetDataUntilCrLf() {
    auto data = input_buffer_.GetDataUntilCrLf();

    if(data.first != nullptr) {
        return data.first;
    }
}